#region Using References

using System;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Game.Converters;
using Beaker.OpenCube.Mapping;

#endregion

namespace Beaker.OpenCube.Game
{
	public class PlayerData
		: MobData
	{
		[Mapping( Ignore = true )] // override so we can make 'remove' the id property for players
		public override string Id
		{
			get
			{
				return base.Id;
			}
			set
			{
				base.Id = value;
			}
		}

		private Dimension _dimension;

		/// <summary>
		/// Gets or sets the dimension the player is in.
		/// </summary>
		/// <value>
		/// The dimension the player is in.
		/// </value>
		public Dimension Dimension
		{
			get
			{
				Contract.Ensures( Contract.Result<Dimension>() == _dimension );
				return _dimension;
			}
			set
			{
				Contract.Ensures( _dimension == value );
				SetValue( ref _dimension, value );
			}
		}

		private GameMode _playerGameMode;

		/// <summary>
		/// Gets or sets the game mode of the player.
		/// </summary>
		/// <returns>
		/// The game mode of the player.
		/// </returns>
		[Mapping( Name = "playerGameType" )]
		public GameMode PlayerGameMode
		{
			get
			{
				Contract.Ensures( Contract.Result<GameMode>() == _playerGameMode );
				return _playerGameMode;
			}
			set
			{
				Contract.Ensures( _playerGameMode == value );
				SetValue( ref _playerGameMode, value );
			}
		}

		private int _score;

		/// <summary>
		/// Gets or sets the score of the player.
		/// </summary>
		/// <value>
		/// The score of the player.
		/// </value>
		[Mapping( IsOptional = true )]
		public int Score
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _score );
				Contract.Ensures( Contract.Result<int>() >= 0 );
				return _score;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= 0, "Score cannot be negative" );
				Contract.Ensures( _score == value );
				SetValue( ref _score, value );
			}
		}

		private int _selectedItemSlot;

		/// <summary>
		/// Gets or sets the selected item slot in the hotbar of the player.
		/// </summary>
		/// <value>
		/// The selected item slot in the hotbar of the player.
		/// </value>
		[Mapping( IsOptional = true )]
		public int SelectedItemSlot
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _selectedItemSlot );
				Contract.Ensures( Contract.Result<int>() >= 0 );
				return _selectedItemSlot;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= 0, "Selected item slot cannot be negative" );
				Contract.Ensures( _selectedItemSlot == value );
				SetValue( ref _selectedItemSlot, value );
			}
		}

		private Coordinates3<int> _spawnPosition;

		/// <summary>
		/// Gets or sets the spawn position of the player.
		/// </summary>
		/// <value>
		/// The spawn position of the player.
		/// </value>
		[Mapping( Names = new[] { "SpawnX", "SpawnY", "SpawnZ" }, IsOptional = true, Converter = typeof( Coordinates3Converter<int> ) )]
		public Coordinates3<int> SpawnPosition
		{
			get
			{
				Contract.Ensures( Contract.Result<Coordinates3<int>>() == _spawnPosition );
				return _spawnPosition;
			}
			set
			{
				Contract.Ensures( _spawnPosition == value );
				SetValue( ref _spawnPosition, value );
			}
		}

		private bool _isSpawnForced;

		/// <summary>
		/// Gets or sets if the player should spawn at the <see cref="SpawnPosition"/> coordinates even if no bed can be found.
		/// </summary>
		/// <value>
		/// <c>true</c> if the player should spawn at the <see cref="SpawnPosition"/> coordinates even if no bed can be found; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "SpawnForced", IsOptional = true )]
		public bool IsSpawnForced
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isSpawnForced );
				return _isSpawnForced;
			}
			set
			{
				Contract.Ensures( _isSpawnForced == value );
				SetValue( ref _isSpawnForced, value );
			}
		}

		private bool _isSleeping;

		/// <summary>
		/// Gets or sets if the person is sleeping.
		/// </summary>
		/// <value>
		/// <c>true</c> if the person is sleeping; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "Sleeping" )]
		public bool IsSleeping
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isSleeping );
				return _isSleeping;
			}
			set
			{
				Contract.Ensures( _isSleeping == value );
				SetValue( ref _isSleeping, value );
			}
		}

		private TimeSpan _sleepTimer;

		/// <summary>
		/// Gets or sets the time the player has been in bed.
		/// </summary>
		/// <value>
		/// The time the player has been in bed.
		/// </value>
		/// <remarks>
		/// Has no effect on whether the player is in a bed when they log in.
		/// </remarks>
		[Mapping( Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan SleepTimer
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() == _sleepTimer );
				return _sleepTimer;
			}

			set
			{
				Contract.Ensures( _sleepTimer == value );
				SetValue( ref _sleepTimer, value );
			}
		}


	}
}